/*
 * Copyright (c) 2009-2011, Peter Abeles. All Rights Reserved.
 *
 * This file is part of Efficient Java Matrix Library (EJML).
 *
 * EJML is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3
 * of the License, or (at your option) any later version.
 *
 * EJML is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with EJML.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.ejml.alg.block;

/**
 * <p>
 * Matrix multiplication for the inner row major blocks, typically inside of a {@link org.ejml.data.BlockMatrix64F}.
 * </p>
 *
 * <p>
 * This code was auto generated by {@link GeneratorBlockInnerMultiplication} and should not be modified directly.
 * </p>
 *
 * @author Peter Abeles
 */
public class BlockInnerMultiplication {

    /**
     * <p>
     * Performs the follow operation on individual inner blocks:<br>
     * <br>
     * C = C + A * B
     * </p>
     */
    public static void blockMultPlus( final double[] dataA, final double []dataB, final double []dataC,
                                     int indexA, int indexB, int indexC,
                                     final int heightA, final int widthA, final int widthC) {
//        for( int i = 0; i < heightA; i++ ) {
//            for( int k = 0; k < widthA; k++ ) {
//                for( int j = 0; j < widthC; j++ ) {
//                    dataC[ i*widthC + j + indexC ] += dataA[i*widthA + k + indexA] * dataB[k*widthC + j + indexB];
//                }
//            }
//        }

        int a = indexA;
        int rowC = indexC;
        for( int i = 0; i < heightA; i++ , rowC += widthC ) {
            int b = indexB;

            final int endC = rowC + widthC;
            final int endA = a + widthA;
            while( a != endA ) {//for( int k = 0; k < widthA; k++ ) {
                double valA = dataA[a++];

                int c = rowC;

                while( c != endC  ) {//for( int j = 0; j < widthC; j++ ) {
                    dataC[ c++ ] += valA * dataB[ b++ ];
                }
            }
        }
    }

    /**
     * <p>
     * Performs the follow operation on individual inner blocks:<br>
     * <br>
     * C = C + A<sup>T</sup> * B
     * </p>
     */
    public static void blockMultPlusTransA( final double[] dataA, final double []dataB, final double []dataC,
                                     int indexA, int indexB, int indexC,
                                     final int heightA, final int widthA, final int widthC) {
//        for( int i = 0; i < widthA; i++ ) {
//            for( int k = 0; k < heightA; k++ ) {
//                double valA = dataA[k*widthA + i + indexA];
//                for( int j = 0; j < widthC; j++ ) {
//                    dataC[ i*widthC + j + indexC ] += valA * dataB[k*widthC + j + indexB];
//                }
//            }
//        }

        int rowC = indexC;
        for( int i = 0; i < widthA; i++ , rowC += widthC) {
            int colA = i + indexA;
            int endA = colA + widthA*heightA;
            int b = indexB;

            // for( int k = 0; k < heightA; k++ ) {
            while(colA != endA ) {
                double valA = dataA[colA];

                int c = rowC;
                final int endB = b + widthC;

                //for( int j = 0; j < widthC; j++ ) {
                while( b != endB ) {
                    dataC[ c++ ] += valA * dataB[b++];
                }
                colA += widthA;
            }
        }
    }

    /**
     * <p>
     * Performs the follow operation on individual inner blocks:<br>
     * <br>
     * C = C + A * B<sup>T</sup>
     * </p>
     */
    public static void blockMultPlusTransB( final double[] dataA, final double []dataB, final double []dataC,
                                     int indexA, int indexB, int indexC,
                                     final int heightA, final int widthA, final int widthC) {
        for( int i = 0; i < heightA; i++ ) {
            for( int j = 0; j < widthC; j++ ) {
                double val = 0;

                for( int k = 0; k < widthA; k++ ) {
                    val += dataA[i*widthA + k + indexA] * dataB[j*widthA + k + indexB];
                }

                dataC[ i*widthC + j + indexC ] += val;
            }
        }
    }

    /**
     * <p>
     * Performs the follow operation on individual inner blocks:<br>
     * <br>
     * C = C - A * B
     * </p>
     */
    public static void blockMultMinus( final double[] dataA, final double []dataB, final double []dataC,
                                     int indexA, int indexB, int indexC,
                                     final int heightA, final int widthA, final int widthC) {
//        for( int i = 0; i < heightA; i++ ) {
//            for( int k = 0; k < widthA; k++ ) {
//                for( int j = 0; j < widthC; j++ ) {
//                    dataC[ i*widthC + j + indexC ] += dataA[i*widthA + k + indexA] * dataB[k*widthC + j + indexB];
//                }
//            }
//        }

        int a = indexA;
        int rowC = indexC;
        for( int i = 0; i < heightA; i++ , rowC += widthC ) {
            int b = indexB;

            final int endC = rowC + widthC;
            final int endA = a + widthA;
            while( a != endA ) {//for( int k = 0; k < widthA; k++ ) {
                double valA = dataA[a++];

                int c = rowC;

                while( c != endC  ) {//for( int j = 0; j < widthC; j++ ) {
                    dataC[ c++ ] -= valA * dataB[ b++ ];
                }
            }
        }
    }

    /**
     * <p>
     * Performs the follow operation on individual inner blocks:<br>
     * <br>
     * C = C - A<sup>T</sup> * B
     * </p>
     */
    public static void blockMultMinusTransA( final double[] dataA, final double []dataB, final double []dataC,
                                     int indexA, int indexB, int indexC,
                                     final int heightA, final int widthA, final int widthC) {
//        for( int i = 0; i < widthA; i++ ) {
//            for( int k = 0; k < heightA; k++ ) {
//                double valA = dataA[k*widthA + i + indexA];
//                for( int j = 0; j < widthC; j++ ) {
//                    dataC[ i*widthC + j + indexC ] += valA * dataB[k*widthC + j + indexB];
//                }
//            }
//        }

        int rowC = indexC;
        for( int i = 0; i < widthA; i++ , rowC += widthC) {
            int colA = i + indexA;
            int endA = colA + widthA*heightA;
            int b = indexB;

            // for( int k = 0; k < heightA; k++ ) {
            while(colA != endA ) {
                double valA = dataA[colA];

                int c = rowC;
                final int endB = b + widthC;

                //for( int j = 0; j < widthC; j++ ) {
                while( b != endB ) {
                    dataC[ c++ ] -= valA * dataB[b++];
                }
                colA += widthA;
            }
        }
    }

    /**
     * <p>
     * Performs the follow operation on individual inner blocks:<br>
     * <br>
     * C = C - A * B<sup>T</sup>
     * </p>
     */
    public static void blockMultMinusTransB( final double[] dataA, final double []dataB, final double []dataC,
                                     int indexA, int indexB, int indexC,
                                     final int heightA, final int widthA, final int widthC) {
        for( int i = 0; i < heightA; i++ ) {
            for( int j = 0; j < widthC; j++ ) {
                double val = 0;

                for( int k = 0; k < widthA; k++ ) {
                    val += dataA[i*widthA + k + indexA] * dataB[j*widthA + k + indexB];
                }

                dataC[ i*widthC + j + indexC ] -= val;
            }
        }
    }

    /**
     * <p>
     * Performs the follow operation on individual inner blocks:<br>
     * <br>
     * C = A * B
     * </p>
     */
    public static void blockMultSet( final double[] dataA, final double []dataB, final double []dataC,
                                     int indexA, int indexB, int indexC,
                                     final int heightA, final int widthA, final int widthC) {
//        for( int i = 0; i < heightA; i++ ) {
//            for( int k = 0; k < widthA; k++ ) {
//                for( int j = 0; j < widthC; j++ ) {
//                    dataC[ i*widthC + j + indexC ] += dataA[i*widthA + k + indexA] * dataB[k*widthC + j + indexB];
//                }
//            }
//        }

        int a = indexA;
        int rowC = indexC;
        for( int i = 0; i < heightA; i++ , rowC += widthC ) {
            int b = indexB;

            final int endC = rowC + widthC;
            final int endA = a + widthA;
            while( a != endA ) {//for( int k = 0; k < widthA; k++ ) {
                double valA = dataA[a++];

                int c = rowC;

                if( b == indexB ) {
                    while( c != endC  ) {//for( int j = 0; j < widthC; j++ ) {
                        dataC[ c++ ] = valA * dataB[ b++ ];
                    }
                } else {
                    while( c != endC  ) {//for( int j = 0; j < widthC; j++ ) {
                        dataC[ c++ ] += valA * dataB[ b++ ];
                    }
                }
            }
        }
    }

    /**
     * <p>
     * Performs the follow operation on individual inner blocks:<br>
     * <br>
     * C = A<sup>T</sup> * B
     * </p>
     */
    public static void blockMultSetTransA( final double[] dataA, final double []dataB, final double []dataC,
                                     int indexA, int indexB, int indexC,
                                     final int heightA, final int widthA, final int widthC) {
//        for( int i = 0; i < widthA; i++ ) {
//            for( int k = 0; k < heightA; k++ ) {
//                double valA = dataA[k*widthA + i + indexA];
//                for( int j = 0; j < widthC; j++ ) {
//                    dataC[ i*widthC + j + indexC ] += valA * dataB[k*widthC + j + indexB];
//                }
//            }
//        }

        int rowC = indexC;
        for( int i = 0; i < widthA; i++ , rowC += widthC) {
            int colA = i + indexA;
            int endA = colA + widthA*heightA;
            int b = indexB;

            // for( int k = 0; k < heightA; k++ ) {
            while(colA != endA ) {
                double valA = dataA[colA];

                int c = rowC;
                final int endB = b + widthC;

                //for( int j = 0; j < widthC; j++ ) {
                if( b == indexB ) {
                    while( b != endB ) {
                        dataC[ c++ ] = valA * dataB[b++];
                    } 
                } else {
                    while( b != endB ) {
                        dataC[ c++ ] += valA * dataB[b++];
                    }
                }
                colA += widthA;
            }
        }
    }

    /**
     * <p>
     * Performs the follow operation on individual inner blocks:<br>
     * <br>
     * C = A * B<sup>T</sup>
     * </p>
     */
    public static void blockMultSetTransB( final double[] dataA, final double []dataB, final double []dataC,
                                     int indexA, int indexB, int indexC,
                                     final int heightA, final int widthA, final int widthC) {
        for( int i = 0; i < heightA; i++ ) {
            for( int j = 0; j < widthC; j++ ) {
                double val = 0;

                for( int k = 0; k < widthA; k++ ) {
                    val += dataA[i*widthA + k + indexA] * dataB[j*widthA + k + indexB];
                }

                dataC[ i*widthC + j + indexC ] = val;
            }
        }
    }

    /**
     * <p>
     * Performs the follow operation on individual inner blocks:<br>
     * <br>
     * C = C +  &alpha; A * B
     * </p>
     */
    public static void blockMultPlus( double alpha , final double[] dataA, final double []dataB, final double []dataC,
                                     int indexA, int indexB, int indexC,
                                     final int heightA, final int widthA, final int widthC) {
//        for( int i = 0; i < heightA; i++ ) {
//            for( int k = 0; k < widthA; k++ ) {
//                for( int j = 0; j < widthC; j++ ) {
//                    dataC[ i*widthC + j + indexC ] += dataA[i*widthA + k + indexA] * dataB[k*widthC + j + indexB];
//                }
//            }
//        }

        int a = indexA;
        int rowC = indexC;
        for( int i = 0; i < heightA; i++ , rowC += widthC ) {
            int b = indexB;

            final int endC = rowC + widthC;
            final int endA = a + widthA;
            while( a != endA ) {//for( int k = 0; k < widthA; k++ ) {
                double valA = alpha*dataA[a++];

                int c = rowC;

                while( c != endC  ) {//for( int j = 0; j < widthC; j++ ) {
                    dataC[ c++ ] += valA * dataB[ b++ ];
                }
            }
        }
    }

    /**
     * <p>
     * Performs the follow operation on individual inner blocks:<br>
     * <br>
     * C = C +  &alpha; A<sup>T</sup> * B
     * </p>
     */
    public static void blockMultPlusTransA( double alpha , final double[] dataA, final double []dataB, final double []dataC,
                                     int indexA, int indexB, int indexC,
                                     final int heightA, final int widthA, final int widthC) {
//        for( int i = 0; i < widthA; i++ ) {
//            for( int k = 0; k < heightA; k++ ) {
//                double valA = dataA[k*widthA + i + indexA];
//                for( int j = 0; j < widthC; j++ ) {
//                    dataC[ i*widthC + j + indexC ] += valA * dataB[k*widthC + j + indexB];
//                }
//            }
//        }

        int rowC = indexC;
        for( int i = 0; i < widthA; i++ , rowC += widthC) {
            int colA = i + indexA;
            int endA = colA + widthA*heightA;
            int b = indexB;

            // for( int k = 0; k < heightA; k++ ) {
            while(colA != endA ) {
                double valA = alpha*dataA[colA];

                int c = rowC;
                final int endB = b + widthC;

                //for( int j = 0; j < widthC; j++ ) {
                while( b != endB ) {
                    dataC[ c++ ] += valA * dataB[b++];
                }
                colA += widthA;
            }
        }
    }

    /**
     * <p>
     * Performs the follow operation on individual inner blocks:<br>
     * <br>
     * C = C +  &alpha; A * B<sup>T</sup>
     * </p>
     */
    public static void blockMultPlusTransB( double alpha , final double[] dataA, final double []dataB, final double []dataC,
                                     int indexA, int indexB, int indexC,
                                     final int heightA, final int widthA, final int widthC) {
        for( int i = 0; i < heightA; i++ ) {
            for( int j = 0; j < widthC; j++ ) {
                double val = 0;

                for( int k = 0; k < widthA; k++ ) {
                    val += dataA[i*widthA + k + indexA] * dataB[j*widthA + k + indexB];
                }

                dataC[ i*widthC + j + indexC ] += alpha *  val;
            }
        }
    }

    /**
     * <p>
     * Performs the follow operation on individual inner blocks:<br>
     * <br>
     * C =  &alpha; A * B
     * </p>
     */
    public static void blockMultSet( double alpha , final double[] dataA, final double []dataB, final double []dataC,
                                     int indexA, int indexB, int indexC,
                                     final int heightA, final int widthA, final int widthC) {
//        for( int i = 0; i < heightA; i++ ) {
//            for( int k = 0; k < widthA; k++ ) {
//                for( int j = 0; j < widthC; j++ ) {
//                    dataC[ i*widthC + j + indexC ] += dataA[i*widthA + k + indexA] * dataB[k*widthC + j + indexB];
//                }
//            }
//        }

        int a = indexA;
        int rowC = indexC;
        for( int i = 0; i < heightA; i++ , rowC += widthC ) {
            int b = indexB;

            final int endC = rowC + widthC;
            final int endA = a + widthA;
            while( a != endA ) {//for( int k = 0; k < widthA; k++ ) {
                double valA = alpha*dataA[a++];

                int c = rowC;

                if( b == indexB ) {
                    while( c != endC  ) {//for( int j = 0; j < widthC; j++ ) {
                        dataC[ c++ ] = valA * dataB[ b++ ];
                    }
                } else {
                    while( c != endC  ) {//for( int j = 0; j < widthC; j++ ) {
                        dataC[ c++ ] += valA * dataB[ b++ ];
                    }
                }
            }
        }
    }

    /**
     * <p>
     * Performs the follow operation on individual inner blocks:<br>
     * <br>
     * C =  &alpha; A<sup>T</sup> * B
     * </p>
     */
    public static void blockMultSetTransA( double alpha , final double[] dataA, final double []dataB, final double []dataC,
                                     int indexA, int indexB, int indexC,
                                     final int heightA, final int widthA, final int widthC) {
//        for( int i = 0; i < widthA; i++ ) {
//            for( int k = 0; k < heightA; k++ ) {
//                double valA = dataA[k*widthA + i + indexA];
//                for( int j = 0; j < widthC; j++ ) {
//                    dataC[ i*widthC + j + indexC ] += valA * dataB[k*widthC + j + indexB];
//                }
//            }
//        }

        int rowC = indexC;
        for( int i = 0; i < widthA; i++ , rowC += widthC) {
            int colA = i + indexA;
            int endA = colA + widthA*heightA;
            int b = indexB;

            // for( int k = 0; k < heightA; k++ ) {
            while(colA != endA ) {
                double valA = alpha*dataA[colA];

                int c = rowC;
                final int endB = b + widthC;

                //for( int j = 0; j < widthC; j++ ) {
                if( b == indexB ) {
                    while( b != endB ) {
                        dataC[ c++ ] = valA * dataB[b++];
                    } 
                } else {
                    while( b != endB ) {
                        dataC[ c++ ] += valA * dataB[b++];
                    }
                }
                colA += widthA;
            }
        }
    }

    /**
     * <p>
     * Performs the follow operation on individual inner blocks:<br>
     * <br>
     * C =  &alpha; A * B<sup>T</sup>
     * </p>
     */
    public static void blockMultSetTransB( double alpha , final double[] dataA, final double []dataB, final double []dataC,
                                     int indexA, int indexB, int indexC,
                                     final int heightA, final int widthA, final int widthC) {
        for( int i = 0; i < heightA; i++ ) {
            for( int j = 0; j < widthC; j++ ) {
                double val = 0;

                for( int k = 0; k < widthA; k++ ) {
                    val += dataA[i*widthA + k + indexA] * dataB[j*widthA + k + indexB];
                }

                dataC[ i*widthC + j + indexC ] = alpha *  val;
            }
        }
    }
}
